﻿#region Header
// ------------------------ Licence / Copyright ------------------------
// 
// ScrumTable for Scrum Meeting Support
// Copyright © HSR - Hochschule für Technik Rapperswil
// All Rights Reserved
// 
// Author:
//  Michael Gfeller Silvan Gehrig Patrick Boos
// 
// ---------------------------------------------------------------------
#endregion

#region Usings

using System.Collections.Generic;
using System.Linq;
using ScrumTable.Common;
using ScrumTable.Common.Collections;
using ScrumTable.DL.Data.Schema;

#endregion

namespace ScrumTable.DL.Data.Generic.Cache
{
    /// <summary>
    /// This class wraps a team foundation field definition instance.
    /// </summary>
    public class CacheWorkItemType : CacheElementBase
    {
        #region Declarations
        //--------------------------------------------------------------------
        // Declarations
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets the name of the name field (SchemaName of [WorkItem].Name schema field).
        /// </summary>
        public const string NameFieldName = "Name";

        /// <summary>
        /// Gets the name of the description field (SchemaName of [WorkItem].Description schema field).
        /// </summary>
        public const string DescriptionFieldName = "Description";

        /// <summary>
        /// Gets the name of the state field (SchemaName of [WorkItem].State schema field).
        /// </summary>
        public const string StateFieldName = "State";

        /// <summary>
        /// Gets the name of the member id field (SchemaName of [WorkItem].MemberId schema field).
        /// </summary>
        public const string MemberIdFieldName = "MemberId";

        /// <summary>
        /// Gets the name of the iteration id field (SchemaName of [WorkItem].IterationId schema field).
        /// </summary>
        public const string IterationIdFieldName = "IterationId";

        #endregion

        #region Properties
        //--------------------------------------------------------------------
        // Properties
        //--------------------------------------------------------------------

        /// <summary>
        /// Gets the allowed values for this field.
        /// </summary>
        public ICacheElementList<CacheFieldDefinition> FieldDefinitions { get; private set; }

        /// <summary>
        /// Gets the ScrumTable schema associated to a work item type.
        /// </summary>
        public SchemaBase Schema { get; private set; }

        /// <summary>
        /// Gets the name field which contains the title.
        /// </summary>
        public CacheFieldDefinition NameField { get { return GetField(NameFieldName); } }

        /// <summary>
        /// Gets the description field.
        /// </summary>
        public CacheFieldDefinition DescriptionField { get { return GetField(DescriptionFieldName); } }

        /// <summary>
        /// Gets the state (for workflows) field.
        /// </summary>
        public CacheFieldDefinition StateField { get { return GetField(StateFieldName); } }

        /// <summary>
        /// Gets the member id field.
        /// </summary>
        public CacheFieldDefinition MemberIdField { get { return GetField(MemberIdFieldName); } }

        /// <summary>
        /// Gets the underlying workflow for the state transitions.
        /// </summary>
        public CacheWorkflow Workflow { get; private set; }

        /// <summary>
        /// Gets all non predefiend field definitions. Predefined field definitions
        /// are specified directly in the current work item type.
        /// </summary>
        public IEnumerable<CacheFieldDefinition> NonPredefinedFieldDefinitions
        {
            get
            {
                return (from CacheFieldDefinition fieldDef in FieldDefinitions
                        where fieldDef.Name != NameFieldName
                              && fieldDef.Name != DescriptionFieldName
                              && fieldDef.Name != StateFieldName
                              && fieldDef.Name != MemberIdFieldName
                        select fieldDef);
            }
        }

        /// <summary>
        /// Gets all non predefiend field definitions. Predefined field definitions
        /// are specified directly in the current work item type. This field definitions
        /// are indexed by BackEndId.
        /// </summary>
        public IDictionary<string, CacheFieldDefinition> NonPredefinedFieldDefinitionsIndexed { get; private set; }

        /// <summary>
        /// Returns true, if the current type represents the user story type.
        /// </summary>
        public bool IsUserStory
        {
            get { return (Name == OwnerContext.Schema.UserStorySchema.SchemaMappedName); }
        }

        /// <summary>
        /// Returns true, if the current type represents the bug type.
        /// </summary>
        public bool IsBug
        {
            get { return (Name == OwnerContext.Schema.BugSchema.SchemaMappedName); }
        }

        /// <summary>
        /// Returns true, if the current type represents the task type.
        /// </summary>
        public bool IsTask
        {
            get { return (Name == OwnerContext.Schema.TaskSchema.SchemaMappedName); }
        }

        /// <summary>
        /// Returns true if the current work item type is valid. This means it
        /// contains all required fields to work properly.
        /// </summary>
        public bool IsValid
        {
            get
            {
                return NameField != null
                    && DescriptionField != null
                    && StateField != null
                    && MemberIdField != null;
            }
        }

        #endregion

        #region Constructors / Destructor
        //--------------------------------------------------------------------
        // Constructors / Destructor
        //--------------------------------------------------------------------

        /// <summary>
        /// Creates a new CacheWorkItemType instance. This constructor is used in
        /// the current library only.
        /// </summary>
        internal CacheWorkItemType(
            string uniqueId,
            string typeName,
            SchemaBase schema,
            IEnumerable<CacheFieldDefinition> fieldDefinitions,
            CacheWorkflow workflow)
            : base(uniqueId, CacheIdentity.DefaultId, typeName, CacheIdentity.DefaultRevision)
        {
            PreCondition.AssertNotNull(schema, "schema");
            PreCondition.AssertNotNull(workflow, "workflow");

            FieldDefinitions = new CacheElementList<CacheFieldDefinition>(fieldDefinitions, this);
            NonPredefinedFieldDefinitionsIndexed = NonPredefinedFieldDefinitions.Convert(fd => new KeyValuePair<string, CacheFieldDefinition>(fd.BackEndId, fd));
            Schema = schema;
            BackEndId = typeName;

            Workflow = workflow;
            Workflow.Parent = this;
        }

        #endregion

        #region Methods
        //--------------------------------------------------------------------
        // Methods
        //--------------------------------------------------------------------

        internal static IEnumerable<CacheFieldDefinition> LoadFieldDefinitionsFromSchema(CacheElementFactory cacheElementFactory, SchemaBase typeSchema, IEnumerable<CacheFieldDefinition> fieldDefinitions)
        {
            PreCondition.AssertNotNull(cacheElementFactory, "cacheElementFactory");
            PreCondition.AssertNotNull(typeSchema, "typeSchema");
            PreCondition.AssertNotNull(fieldDefinitions, "fieldDefinitions");

            IList<CacheFieldDefinition> fieldList = fieldDefinitions.ToList();

            var requiredSchemata = from schema in typeSchema.Schemata
                                   where !schema.IsCustomSchema && !fieldList.Contains(field => field.Schema == schema)
                                   select schema;

            requiredSchemata.ForEach(schema => fieldList.Add(cacheElementFactory.CreateFieldDefinition(schema)));
            return fieldList;
        }


        /// <summary>
        /// Gets a field definition which corresponds to the specified back
        /// end id (in case of TFS this name is equal to the reference name
        /// defined in the schema). If it does not exist, you will receive a
        /// null pointer.
        /// </summary>
        /// <param name="backEndId">Specifies the back end id of the field.</param>
        public CacheFieldDefinition TryGetFieldByBackEndId(string backEndId)
        {
            return (from field in FieldDefinitions
                    where field.BackEndId == backEndId
                    select field).FirstOrDefault();
        }

        private CacheFieldDefinition GetField(string name)
        {
            return (from field in FieldDefinitions
                    where field.Name == name
                    select field).FirstOrDefault();
        }

        #endregion

        #region Events
        //--------------------------------------------------------------------
        // Events
        //--------------------------------------------------------------------

        #endregion
    }
}